﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;

namespace SHFileInfo.Icon.HttpHandlers
{
    public class SHFileInfoIconsHandler : IHttpHandler
    {
        #region Win32
        private struct SHFileInfo
        {
            internal IntPtr hIcon;
            internal int iIcon;
            internal uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            internal string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            internal string szTypeName;
        };

        private enum IconSize : uint
        {
            SHGFI_LARGEICON = 0x0,
            SHGFI_SMALLICON = 0x1
        }

        private enum FolderType : uint
        {
            SHGFI_OPENICON = 0x2,
            SHGFI_CLOSEICON = 0x0
        }

        private const uint SHGFI_ICON = 0x100;
        private const uint SHGFI_USEFILEATTRIBUTES = 0x10;
        private const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
        private const uint FILE_ATTRIBUTE_NORMAL = 0x80;

        /// <summary>
        /// Retrieves information about an object in the file system, such as a file, folder, directory, or drive root
        /// </summary>
        /// <param name="pszPath">Contains the path and file name. Both absolute and relative paths are valid</param>
        /// <param name="dwFileAttributes">A combination of one or more file attribute flags</param>
        /// <param name="psfi">SHFILEINFO structure to receive the file information</param>
        /// <param name="cbSizeFileInfo">The size, in bytes, of the SHFILEINFO structure</param>
        /// <param name="uFlags">The flags that specify the file information to retrieve</param>
        /// <returns>Meaning depends on the uFlags parameter</returns>
        [DllImport("shell32.dll")]
        static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFileInfo psfi, uint cbSizeFileInfo, uint uFlags);

        /// <summary>
        /// Destroys an icon and frees any memory the icon occupied
        /// </summary>
        /// <param name="handle">Handle to the icon to be destroyed</param>
        /// <returns>If the function succeeds, the return value is nonzero</returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        extern static bool DestroyIcon(IntPtr handle);
        #endregion

        #region Methods
        /// <summary>
        /// Extracting icon for specific extension from the system
        /// </summary>
        /// <param name="extension">file type extenstion</param>
        /// <param name="size">icon size</param>
        /// <returns>system icon</returns>
        private System.Drawing.Icon GetExtensionIcon(string extension, IconSize size)
        {
            try
            {
                System.Drawing.Icon tempIcon;
                SHFileInfo tempFileInfo = new SHFileInfo();

                //Getting SHFileInfo for our extension
                SHGetFileInfo(extension, 0, ref tempFileInfo, (uint)Marshal.SizeOf(tempFileInfo), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | (uint)size);
                //Creating Icon based on SHFileInfo
                tempIcon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(tempFileInfo.hIcon);
                //Making managed clone
                System.Drawing.Icon managedIcon = (System.Drawing.Icon)tempIcon.Clone();
                //Destroying unmanaged handle
                DestroyIcon(tempIcon.Handle);
                return managedIcon;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Extracting icon for folder from the system
        /// </summary>
        /// <param name="size">icon size</param>
        /// <param name="type">folder type</param>
        /// <returns>system icon</returns>
        private System.Drawing.Icon GetFolderIcon(IconSize size, FolderType type)
        {
            try
            {
                System.Drawing.Icon tempIcon;
                SHFileInfo tempFileInfo = new SHFileInfo();

                //Getting SHFileInfo for directory
                SHGetFileInfo(null, FILE_ATTRIBUTE_DIRECTORY, ref tempFileInfo, (uint)Marshal.SizeOf(tempFileInfo), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | (uint)size | (uint)type);
                //Creating Icon based on SHFileInfo
                tempIcon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(tempFileInfo.hIcon);
                //Making managed clone
                System.Drawing.Icon managedIcon = (System.Drawing.Icon)tempIcon.Clone();
                //Destroying unmanaged handle
                DestroyIcon(tempIcon.Handle);
                return managedIcon;
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            System.Drawing.Icon systemIcon = null;
            //Ckecking parameters
            if (!String.IsNullOrEmpty(context.Request.QueryString["type"]) && !String.IsNullOrEmpty(context.Request.QueryString["size"]))
            {
                //Setting icon size
                IconSize size = IconSize.SHGFI_SMALLICON;
                if (context.Request.QueryString["size"].Equals("large"))
                    size = IconSize.SHGFI_LARGEICON;

                //Getting icon
                if (context.Request.QueryString["type"].Equals("dir"))
                    systemIcon = GetFolderIcon(size, FolderType.SHGFI_CLOSEICON);
                else if (context.Request.QueryString["type"].Equals("file"))
                    systemIcon = GetExtensionIcon(context.Request.QueryString["ext"], size);
            }

            if (systemIcon != null)
            {
                MemoryStream systemIconStream = new MemoryStream();

                //Converting icon to .png
                systemIcon.ToBitmap().Save(systemIconStream, ImageFormat.Png);
                systemIconStream.Close();
                byte[] systemIconBytes = systemIconStream.GetBuffer();

                //Setting the response
                context.Response.ContentType = "image/png";
                context.Response.BufferOutput = true;
                context.Response.OutputStream.Write(systemIconBytes, 0, systemIconBytes.Length);
            }
        }
        #endregion
    }
}